/*-------------------------------------------------------------------------
 *
 * reloptions.c
 *	  关系选项的核心支持 (pg_class.reloptions)
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/access/common/reloptions.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include <float.h>

#include "access/gist_private.h"
#include "access/hash.h"
#include "access/heaptoast.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "access/reloptions.h"
#include "access/spgist_private.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/tablespace.h"
#include "commands/view.h"
#include "nodes/makefuncs.h"
#include "postmaster/postmaster.h"
#include "utils/array.h"
#include "utils/attoptcache.h"
#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/rel.h"

/*
 * pg_class.reloptions的内容
 *
 * 添加选项：
 *
 * (i) 决定类型（整数、实数、布尔值、字符串）、名称、默认值、上界和下界（如果适用）；对于字符串，请考虑验证例程。
 * (ii) 在下面添加记录（或使用add_<type>_reloption）。
 * (iii) 将其添加到适当的选项结构中（或许是StdRdOptions）
 * (iv) 将其添加到适当的处理例程中（或许是default_reloptions）
 * (v) 确保锁定级别正确设置针对该操作
 * (vi) 不要忘记记录该选项
 *
 * 任何新选项的默认选择应为AccessExclusiveLock。
 * 在某些情况下，锁定级别可以从那里减少，但选择的锁定级别应始终相互冲突，以确保当我们尝试并发更改时不会丢失多个更改。
 * 锁定级别的选择完全取决于该参数在服务器中的使用方式，而不是您希望如何以及何时更改它。
 * 安全第一。现有的选择在此处记录，以及在后端代码中使用参数的其他地方。
 *
 * 通常，任何影响从SELECT获得的结果的内容必须由AccessExclusiveLock保护。
 *
 * 自动清理相关参数可以在ShareUpdateExclusiveLock下设置，因为它们仅由AV进程使用且不会更改当前执行的内容。
 *
 * Fillfactor可以被设置，因为它仅适用于对数据块所做的后续更改，如hio.c中记录。
 *
 * n_distinct选项可以在ShareUpdateExclusiveLock下设置，因为它们仅在ANALYZE期间使用，ANALYZE使用ShareUpdateExclusiveLock，因此ANALYZE不会受正在进行的更改的影响。更改这些值在下一个ANALYZE之前没有效果，因此不需要更强的锁。
 *
 * 计划相关参数可以使用ShareUpdateExclusiveLock进行设置，因为它们只影响规划而不影响执行的正确性。计划在执行过程中不能更改，因此这里的更改无论如何都不容易导致新的改进计划。所以我们允许现有查询继续执行并使现有计划保持不变，这是一种小代价，允许更好的计划并发引入而不干扰用户。
 *
 * 设置parallel_workers是安全的，因为它的作用与max_parallel_workers_per_gather相同，后者是一种USERSET参数，不会影响现有的计划或查询。
 *
 * vacuum_truncate可以在ShareUpdateExclusiveLock下设置，因为它仅在VACUUM期间使用，VACUUM使用ShareUpdateExclusiveLock，因此VACUUM不会受到正在进行的更改的影响。更改其值在下一个VACUUM之前没有效果，因此不需要更强的锁。
 */

static relopt_bool boolRelOpts[] =
{
	{
		{
			"autosummarize",
			"Enables automatic summarization on this BRIN index",
			RELOPT_KIND_BRIN,
			AccessExclusiveLock
		},
		false
	},
	{
		{
			"autovacuum_enabled",
			"Enables autovacuum in this relation",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		},
		true
	},
	{
		{
			"user_catalog_table",
			"Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
			RELOPT_KIND_HEAP,
			AccessExclusiveLock
		},
		false
	},
	{
		{
			"fastupdate",
			"Enables \"fast update\" feature for this GIN index",
			RELOPT_KIND_GIN,
			AccessExclusiveLock
		},
		true
	},
	{
		{
			"security_barrier",
			"View acts as a row security barrier",
			RELOPT_KIND_VIEW,
			AccessExclusiveLock
		},
		false
	},
	{
		{
			"security_invoker",
			"Privileges on underlying relations are checked as the invoking user, not the view owner",
			RELOPT_KIND_VIEW,
			AccessExclusiveLock
		},
		false
	},
	{
		{
			"vacuum_truncate",
			"Enables vacuum to truncate empty pages at the end of this table",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		},
		true
	},
	{
		{
			"deduplicate_items",
			"Enables \"deduplicate items\" feature for this btree index",
			RELOPT_KIND_BTREE,
			ShareUpdateExclusiveLock	/* 因为它仅适用于后续插入 */
		},
		true
	},
	/* 列表终止符 */
	{{NULL}}
};

static relopt_int intRelOpts[] =
{
	{
		{
			"fillfactor",
			"Packs table pages only to this percentage",
			RELOPT_KIND_HEAP,
			ShareUpdateExclusiveLock	/* 因为它仅适用于后续插入 */
		},
		HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
	},
	{
		{
			"fillfactor",
			"Packs btree index pages only to this percentage",
			RELOPT_KIND_BTREE,
			ShareUpdateExclusiveLock	/* 因为它仅适用于后续插入 */
		},
		BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
	},
	{
		{
			"fillfactor",
			"Packs hash index pages only to this percentage",
			RELOPT_KIND_HASH,
			ShareUpdateExclusiveLock	/* 因为它仅适用于后续插入 */
		},
		HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
	},
	{
		{
			"fillfactor",
			"Packs gist index pages only to this percentage",
			RELOPT_KIND_GIST,
			ShareUpdateExclusiveLock	/* 因为它仅适用于后续插入 */
		},
		GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
	},
	{
		{
			"fillfactor",
			"Packs spgist index pages only to this percentage",
			RELOPT_KIND_SPGIST,
			ShareUpdateExclusiveLock	/* 因为它仅适用于后续插入 */
		},
		SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100
	},
	{
		{
			"autovacuum_vacuum_threshold",
			"Minimum number of tuple updates or deletes prior to vacuum",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		},
		-1, 0, INT_MAX
	},
	{
		{
			"autovacuum_vacuum_insert_threshold",
			"Minimum number of tuple inserts prior to vacuum, or -1 to disable insert vacuums",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		},
		-2, -1, INT_MAX
	},
	{
		{
			"autovacuum_analyze_threshold",
			"Minimum number of tuple inserts, updates or deletes prior to analyze",
			RELOPT_KIND_HEAP,
			ShareUpdateExclusiveLock
		},
		-1, 0, INT_MAX
	},
	{
		{
			"autovacuum_vacuum_cost_limit",
			"Vacuum cost amount available before napping, for autovacuum",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		},
		-1, 1, 10000
	},
	{
		{
			"autovacuum_freeze_min_age",
			"Minimum age at which VACUUM should freeze a table row, for autovacuum",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		},
		-1, 0, 1000000000
	},
	{
		{
			"autovacuum_multixact_freeze_min_age",
			"Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		},
		-1, 0, 1000000000
	},
	{
		{
			"autovacuum_freeze_max_age",
			"Age at which to autovacuum a table to prevent transaction ID wraparound",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		},
		-1, 100000, 2000000000
	},
	{
		{
			"autovacuum_multixact_freeze_max_age",
			"Multixact age at which to autovacuum a table to prevent multixact wraparound",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		},
		-1, 10000, 2000000000
	},
	{
		{
			"autovacuum_freeze_table_age",
			"Age at which VACUUM should perform a full table sweep to freeze row versions",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		}, -1, 0, 2000000000
	},
	{
		{
			"autovacuum_multixact_freeze_table_age",
			"Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		}, -1, 0, 2000000000
	},
	{
		{
			"log_autovacuum_min_duration",
			"Sets the minimum execution time above which autovacuum actions will be logged",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		},
		-1, -1, INT_MAX
	},
	{
		{
			"toast_tuple_target",
			"Sets the target tuple length at which external columns will be toasted",
			RELOPT_KIND_HEAP,
			ShareUpdateExclusiveLock
		},
		TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
	},
	{
		{
			"pages_per_range",
			"Number of pages that each page range covers in a BRIN index",
			RELOPT_KIND_BRIN,
			AccessExclusiveLock
		}, 128, 1, 131072
	},
	{
		{
			"gin_pending_list_limit",
			"Maximum size of the pending list for this GIN index, in kilobytes.",
			RELOPT_KIND_GIN,
			AccessExclusiveLock
		},
		-1, 64, MAX_KILOBYTES
	},
	{
		{
			"effective_io_concurrency",
			"Number of simultaneous requests that can be handled efficiently by the disk subsystem.",
			RELOPT_KIND_TABLESPACE,
			ShareUpdateExclusiveLock
		},
#ifdef USE_PREFETCH
		-1, 0, MAX_IO_CONCURRENCY
#else
		0, 0, 0
#endif
	},
	{
		{
			"maintenance_io_concurrency",
			"Number of simultaneous requests that can be handled efficiently by the disk subsystem for maintenance work.",
			RELOPT_KIND_TABLESPACE,
			ShareUpdateExclusiveLock
		},
#ifdef USE_PREFETCH
		-1, 0, MAX_IO_CONCURRENCY
#else
		0, 0, 0
#endif
	},
	{
		{
			"parallel_workers",
			"Number of parallel processes that can be used per executor node for this relation.",
			RELOPT_KIND_HEAP,
			ShareUpdateExclusiveLock
		},
		-1, 0, 1024
	},

	/* 列表终止符 */
	{{NULL}}
};

static relopt_real realRelOpts[] =
{
	{
		{
			"autovacuum_vacuum_cost_delay",
			"Vacuum cost delay in milliseconds, for autovacuum",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		},
		-1, 0.0, 100.0
	},
	{
		{
			"autovacuum_vacuum_scale_factor",
			"Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		},
		-1, 0.0, 100.0
	},
	{
		{
			"autovacuum_vacuum_insert_scale_factor",
			"Number of tuple inserts prior to vacuum as a fraction of reltuples",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		},
		-1, 0.0, 100.0
	},
	{
		{
			"autovacuum_analyze_scale_factor",
			"Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
			RELOPT_KIND_HEAP,
			ShareUpdateExclusiveLock
		},
		-1, 0.0, 100.0
	},
	{
		{
			"seq_page_cost",
			"Sets the planner's estimate of the cost of a sequentially fetched disk page.",
			RELOPT_KIND_TABLESPACE,
			ShareUpdateExclusiveLock
		},
		-1, 0.0, DBL_MAX
	},
	{
		{
			"random_page_cost",
			"Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
			RELOPT_KIND_TABLESPACE,
			ShareUpdateExclusiveLock
		},
		-1, 0.0, DBL_MAX
	},
	{
		{
			"n_distinct",
			"Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
			RELOPT_KIND_ATTRIBUTE,
			ShareUpdateExclusiveLock
		},
		0, -1.0, DBL_MAX
	},
	{
		{
			"n_distinct_inherited",
			"Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
			RELOPT_KIND_ATTRIBUTE,
			ShareUpdateExclusiveLock
		},
		0, -1.0, DBL_MAX
	},
	{
		{
			"vacuum_cleanup_index_scale_factor",
			"Deprecated B-Tree parameter.",
			RELOPT_KIND_BTREE,
			ShareUpdateExclusiveLock
		},
		-1, 0.0, 1e10
	},
	/* 列表终止符 */
	{{NULL}}
};

/* 来自 StdRdOptIndexCleanup 的值 */
static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
{
	{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
	{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
	{"off", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
	{"true", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
	{"false", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
	{"yes", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
	{"no", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
	{"1", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
	{"0", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
	{(const char *) NULL}		/* 列表终止符 */
};

/* 来自 GistOptBufferingMode 的值 */
static relopt_enum_elt_def gistBufferingOptValues[] =
{
	{"auto", GIST_OPTION_BUFFERING_AUTO},
	{"on", GIST_OPTION_BUFFERING_ON},
	{"off", GIST_OPTION_BUFFERING_OFF},
	{(const char *) NULL}		/* 列表终止符 */
};

/* 来自 ViewOptCheckOption 的值 */
static relopt_enum_elt_def viewCheckOptValues[] =
{
	/* NOT_SET 没有值 */
	{"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
	{"cascaded", VIEW_OPTION_CHECK_OPTION_CASCADED},
	{(const char *) NULL}		/* 列表终止符 */
};

static relopt_enum enumRelOpts[] =
{
	{
		{
			"vacuum_index_cleanup",
			"Controls index vacuuming and index cleanup",
			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
			ShareUpdateExclusiveLock
		},
		StdRdOptIndexCleanupValues,
		STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO,
		gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
	},
	{
		{
			"buffering",
			"Enables buffering build for this GiST index",
			RELOPT_KIND_GIST,
			AccessExclusiveLock
		},
		gistBufferingOptValues,
		GIST_OPTION_BUFFERING_AUTO,
		gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
	},
	{
		{
			"check_option",
			"View has WITH CHECK OPTION defined (local or cascaded).",
			RELOPT_KIND_VIEW,
			AccessExclusiveLock
		},
		viewCheckOptValues,
		VIEW_OPTION_CHECK_OPTION_NOT_SET,
		gettext_noop("Valid values are \"local\" and \"cascaded\".")
	},
	/* 列表终止符 */
	{{NULL}}
};

static relopt_string stringRelOpts[] =
{
	/* 列表终止符 */
	{{NULL}}
};

static relopt_gen **relOpts = NULL;
static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;

static int	num_custom_options = 0;
static relopt_gen **custom_options = NULL;
static bool need_initialization = true;

static void fc_initialize_reloptions(void);
static void fc_parse_one_reloption(relopt_value *fc_option, char *fc_text_str,
								int fc_text_len, bool fc_validate);

/*
 * 获取字符串 reloption 的长度（默认为用户定义的值）。这用于在构建关系选项集时进行分配。
 */
#define GET_STRING_RELOPTION_LEN(option) \
	((option).isset ? strlen((option).values.string_val) : \
	 ((relopt_string *) (option).gen)->default_len)

/*
 * initialize_reloptions
 *		初始化例程，必须在解析之前调用
 *
 * 初始化 relOpts 数组并填充每个变量的类型和名称长度。
 */
static void fc_initialize_reloptions(void)
{
	int			fc_i;
	int			fc_j;

	fc_j = 0;
	for (fc_i = 0; boolRelOpts[fc_i].gen.name; fc_i++)
	{
		Assert(DoLockModesConflict(boolRelOpts[fc_i].gen.lockmode,
								   boolRelOpts[fc_i].gen.lockmode));
		fc_j++;
	}
	for (fc_i = 0; intRelOpts[fc_i].gen.name; fc_i++)
	{
		Assert(DoLockModesConflict(intRelOpts[fc_i].gen.lockmode,
								   intRelOpts[fc_i].gen.lockmode));
		fc_j++;
	}
	for (fc_i = 0; realRelOpts[fc_i].gen.name; fc_i++)
	{
		Assert(DoLockModesConflict(realRelOpts[fc_i].gen.lockmode,
								   realRelOpts[fc_i].gen.lockmode));
		fc_j++;
	}
	for (fc_i = 0; enumRelOpts[fc_i].gen.name; fc_i++)
	{
		Assert(DoLockModesConflict(enumRelOpts[fc_i].gen.lockmode,
								   enumRelOpts[fc_i].gen.lockmode));
		fc_j++;
	}
	for (fc_i = 0; stringRelOpts[fc_i].gen.name; fc_i++)
	{
		Assert(DoLockModesConflict(stringRelOpts[fc_i].gen.lockmode,
								   stringRelOpts[fc_i].gen.lockmode));
		fc_j++;
	}
	fc_j += num_custom_options;

	if (relOpts)
		pfree(relOpts);
	relOpts = MemoryContextAlloc(TopMemoryContext,
								 (fc_j + 1) * sizeof(relopt_gen *));

	fc_j = 0;
	for (fc_i = 0; boolRelOpts[fc_i].gen.name; fc_i++)
	{
		relOpts[fc_j] = &boolRelOpts[fc_i].gen;
		relOpts[fc_j]->type = RELOPT_TYPE_BOOL;
		relOpts[fc_j]->namelen = strlen(relOpts[fc_j]->name);
		fc_j++;
	}

	for (fc_i = 0; intRelOpts[fc_i].gen.name; fc_i++)
	{
		relOpts[fc_j] = &intRelOpts[fc_i].gen;
		relOpts[fc_j]->type = RELOPT_TYPE_INT;
		relOpts[fc_j]->namelen = strlen(relOpts[fc_j]->name);
		fc_j++;
	}

	for (fc_i = 0; realRelOpts[fc_i].gen.name; fc_i++)
	{
		relOpts[fc_j] = &realRelOpts[fc_i].gen;
		relOpts[fc_j]->type = RELOPT_TYPE_REAL;
		relOpts[fc_j]->namelen = strlen(relOpts[fc_j]->name);
		fc_j++;
	}

	for (fc_i = 0; enumRelOpts[fc_i].gen.name; fc_i++)
	{
		relOpts[fc_j] = &enumRelOpts[fc_i].gen;
		relOpts[fc_j]->type = RELOPT_TYPE_ENUM;
		relOpts[fc_j]->namelen = strlen(relOpts[fc_j]->name);
		fc_j++;
	}

	for (fc_i = 0; stringRelOpts[fc_i].gen.name; fc_i++)
	{
		relOpts[fc_j] = &stringRelOpts[fc_i].gen;
		relOpts[fc_j]->type = RELOPT_TYPE_STRING;
		relOpts[fc_j]->namelen = strlen(relOpts[fc_j]->name);
		fc_j++;
	}

	for (fc_i = 0; fc_i < num_custom_options; fc_i++)
	{
		relOpts[fc_j] = custom_options[fc_i];
		fc_j++;
	}

	/* 添加列表终止符 */
	relOpts[fc_j] = NULL;

	/* 标记工作已完成 */
	need_initialization = false;
}

/*
 * add_reloption_kind
 *		创建一个新的 relopt_kind 值，以供用户定义的 AM 使用自定义 reloptions。
 */
relopt_kind
add_reloption_kind(void)
{
	/* 不分发最后一部分，以便枚举的行为是可移植的 */
	if (last_assigned_kind >= RELOPT_KIND_MAX)
		ereport(ERROR,
				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
				 errmsg("user-defined relation parameter types limit exceeded")));
	last_assigned_kind <<= 1;
	return (relopt_kind) last_assigned_kind;
}

/*
 * add_reloption
 *		将已创建的自定义 reloption 添加到列表中，并重新计算主解析器表。
 */
static void fc_add_reloption(relopt_gen *fc_newoption)
{
	static int	fc_max_custom_options = 0;

	if (num_custom_options >= fc_max_custom_options)
	{
		MemoryContext fc_oldcxt;

		fc_oldcxt = MemoryContextSwitchTo(TopMemoryContext);

		if (fc_max_custom_options == 0)
		{
			fc_max_custom_options = 8;
			custom_options = palloc(fc_max_custom_options * sizeof(relopt_gen *));
		}
		else
		{
			fc_max_custom_options *= 2;
			custom_options = repalloc(custom_options,
									  fc_max_custom_options * sizeof(relopt_gen *));
		}
		MemoryContextSwitchTo(fc_oldcxt);
	}
	custom_options[num_custom_options++] = fc_newoption;

	need_initialization = true;
}

/*
 * init_local_reloptions
 *		初始化将解析为 'relopt_struct_size' 的 bytea 结构的本地 reloptions。
 */
void init_local_reloptions(local_relopts *fc_opts, Size fc_relopt_struct_size)
{
	fc_opts->options = NIL;
	fc_opts->validators = NIL;
	fc_opts->relopt_struct_size = fc_relopt_struct_size;
}

/*
 * register_reloptions_validator
 *		注册自定义验证回调，该回调将在 build_local_reloptions() 结束时调用。
 */
void register_reloptions_validator(local_relopts *fc_opts, relopts_validator fc_validator)
{
	fc_opts->validators = lappend(fc_opts->validators, fc_validator);
}

/*
 * add_local_reloption
 *		将已创建的自定义 reloption 添加到本地列表中。
 */
static void fc_add_local_reloption(local_relopts *fc_relopts, relopt_gen *fc_newoption, int fc_offset)
{
	local_relopt *fc_opt = palloc(sizeof(*fc_opt));

	Assert(fc_offset < fc_relopts->relopt_struct_size);

	fc_opt->option = fc_newoption;
	fc_opt->offset = fc_offset;

	fc_relopts->options = lappend(fc_relopts->options, fc_opt);
}

/*
 * allocate_reloption
 *		分配一个新的 reloption 并初始化与类型无关的字段（对于字符串以外的类型）
 */
static relopt_gen *
fc_allocate_reloption(bits32 fc_kinds, int fc_type, const char *fc_name, const char *fc_desc,
				   LOCKMODE fc_lockmode)
{
	MemoryContext fc_oldcxt;
	size_t		fc_size;
	relopt_gen *fc_newoption;

	if (fc_kinds != RELOPT_KIND_LOCAL)
		fc_oldcxt = MemoryContextSwitchTo(TopMemoryContext);
	else
		fc_oldcxt = NULL;

	switch (fc_type)
	{
		case RELOPT_TYPE_BOOL:
			fc_size = sizeof(relopt_bool);
			break;
		case RELOPT_TYPE_INT:
			fc_size = sizeof(relopt_int);
			break;
		case RELOPT_TYPE_REAL:
			fc_size = sizeof(relopt_real);
			break;
		case RELOPT_TYPE_ENUM:
			fc_size = sizeof(relopt_enum);
			break;
		case RELOPT_TYPE_STRING:
			fc_size = sizeof(relopt_string);
			break;
		default:
			elog(ERROR, "unsupported reloption type %d", fc_type);
			return NULL;		/* 保持编译器安静 */
	}

	fc_newoption = palloc(fc_size);

	fc_newoption->name = pstrdup(fc_name);
	if (fc_desc)
		fc_newoption->desc = pstrdup(fc_desc);
	else
		fc_newoption->desc = NULL;
	fc_newoption->kinds = fc_kinds;
	fc_newoption->namelen = strlen(fc_name);
	fc_newoption->type = fc_type;
	fc_newoption->lockmode = fc_lockmode;

	if (fc_oldcxt != NULL)
		MemoryContextSwitchTo(fc_oldcxt);

	return fc_newoption;
}

/*
 * init_bool_reloption
 *		分配和初始化一个新的布尔型 reloption。
 */
static relopt_bool *
fc_init_bool_reloption(bits32 fc_kinds, const char *fc_name, const char *fc_desc,
					bool fc_default_val, LOCKMODE fc_lockmode)
{
	relopt_bool *fc_newoption;

	fc_newoption = (relopt_bool *) fc_allocate_reloption(fc_kinds, RELOPT_TYPE_BOOL,
												   fc_name, fc_desc, fc_lockmode);
	fc_newoption->default_val = fc_default_val;

	return fc_newoption;
}

/*
 * add_bool_reloption
 *		添加一个新的布尔型 reloption。
 */
void add_bool_reloption(bits32 fc_kinds, const char *fc_name, const char *fc_desc,
				   bool fc_default_val, LOCKMODE fc_lockmode)
{
	relopt_bool *fc_newoption = fc_init_bool_reloption(fc_kinds, fc_name, fc_desc,
												 fc_default_val, fc_lockmode);

	fc_add_reloption((relopt_gen *) fc_newoption);
}

/*
 * add_local_bool_reloption
 *		添加一个新的本地布尔型 reloption。
 *
 * 'offset' 是布尔类型字段的偏移量。
 */
void add_local_bool_reloption(local_relopts *fc_relopts, const char *fc_name,
						 const char *fc_desc, bool fc_default_val, int fc_offset)
{
	relopt_bool *fc_newoption = fc_init_bool_reloption(RELOPT_KIND_LOCAL,
												 fc_name, fc_desc,
												 fc_default_val, 0);

	fc_add_local_reloption(fc_relopts, (relopt_gen *) fc_newoption, fc_offset);
}


/*
 * init_real_reloption
 *		分配和初始化一个新的整数型 reloption。
 */
static relopt_int *
fc_init_int_reloption(bits32 fc_kinds, const char *fc_name, const char *fc_desc,
				   int fc_default_val, int fc_min_val, int fc_max_val,
				   LOCKMODE fc_lockmode)
{
	relopt_int *fc_newoption;

	fc_newoption = (relopt_int *) fc_allocate_reloption(fc_kinds, RELOPT_TYPE_INT,
												  fc_name, fc_desc, fc_lockmode);
	fc_newoption->default_val = fc_default_val;
	fc_newoption->min = fc_min_val;
	fc_newoption->max = fc_max_val;

	return fc_newoption;
}

/*
 * add_int_reloption
 *		添加一个新的整数型 reloption。
 */
void add_int_reloption(bits32 fc_kinds, const char *fc_name, const char *fc_desc, int fc_default_val,
				  int fc_min_val, int fc_max_val, LOCKMODE fc_lockmode)
{
	relopt_int *fc_newoption = fc_init_int_reloption(fc_kinds, fc_name, fc_desc,
											   fc_default_val, fc_min_val,
											   fc_max_val, fc_lockmode);

	fc_add_reloption((relopt_gen *) fc_newoption);
}

/*
 * add_local_int_reloption
 *		添加一个新的本地整数型 reloption。
 *
 * 'offset' 是整数类型字段的偏移量。
 */
void add_local_int_reloption(local_relopts *fc_relopts, const char *fc_name,
						const char *fc_desc, int fc_default_val, int fc_min_val,
						int fc_max_val, int fc_offset)
{
	relopt_int *fc_newoption = fc_init_int_reloption(RELOPT_KIND_LOCAL,
											   fc_name, fc_desc, fc_default_val,
											   fc_min_val, fc_max_val, 0);

	fc_add_local_reloption(fc_relopts, (relopt_gen *) fc_newoption, fc_offset);
}

/*
 * init_real_reloption
 *		分配和初始化一个新的实数型 reloption。
 */
static relopt_real *
fc_init_real_reloption(bits32 fc_kinds, const char *fc_name, const char *fc_desc,
					double fc_default_val, double fc_min_val, double fc_max_val,
					LOCKMODE fc_lockmode)
{
	relopt_real *fc_newoption;

	fc_newoption = (relopt_real *) fc_allocate_reloption(fc_kinds, RELOPT_TYPE_REAL,
												   fc_name, fc_desc, fc_lockmode);
	fc_newoption->default_val = fc_default_val;
	fc_newoption->min = fc_min_val;
	fc_newoption->max = fc_max_val;

	return fc_newoption;
}

/*
 * add_real_reloption
 *		添加一个新的浮点型 reloption。
 */
void add_real_reloption(bits32 fc_kinds, const char *fc_name, const char *fc_desc,
				   double fc_default_val, double fc_min_val, double fc_max_val,
				   LOCKMODE fc_lockmode)
{
	relopt_real *fc_newoption = fc_init_real_reloption(fc_kinds, fc_name, fc_desc,
												 fc_default_val, fc_min_val,
												 fc_max_val, fc_lockmode);

	fc_add_reloption((relopt_gen *) fc_newoption);
}

/*
 * add_local_real_reloption
 *		添加一个新的本地浮点型 reloption。
 *
 * 'offset' 是双精度类型字段的偏移量。
 */
void add_local_real_reloption(local_relopts *fc_relopts, const char *fc_name,
						 const char *fc_desc, double fc_default_val,
						 double fc_min_val, double fc_max_val, int fc_offset)
{
	relopt_real *fc_newoption = fc_init_real_reloption(RELOPT_KIND_LOCAL,
												 fc_name, fc_desc,
												 fc_default_val, fc_min_val,
												 fc_max_val, 0);

	fc_add_local_reloption(fc_relopts, (relopt_gen *) fc_newoption, fc_offset);
}

/*
 * init_enum_reloption
 *		分配和初始化一个新的枚举型 reloption。
 */
static relopt_enum *
fc_init_enum_reloption(bits32 fc_kinds, const char *fc_name, const char *fc_desc,
					relopt_enum_elt_def *fc_members, int fc_default_val,
					const char *fc_detailmsg, LOCKMODE fc_lockmode)
{
	relopt_enum *fc_newoption;

	fc_newoption = (relopt_enum *) fc_allocate_reloption(fc_kinds, RELOPT_TYPE_ENUM,
												   fc_name, fc_desc, fc_lockmode);
	fc_newoption->members = fc_members;
	fc_newoption->default_val = fc_default_val;
	fc_newoption->detailmsg = fc_detailmsg;

	return fc_newoption;
}


/*
 * add_enum_reloption
 *		添加一个新的枚举 reloption
 *
 * 成员数组必须有一个终止的 NULL 条目。
 *
 * 当传入不支持的值时，detailmsg 将显示，格式为：“有效值是 \"foo\", \"bar\", 和 \"bar\"。”
 *
 * 成员数组和 detailmsg 不会被复制 -- 调用者必须确保它们在进程的整个生命周期内有效。
 */
void add_enum_reloption(bits32 fc_kinds, const char *fc_name, const char *fc_desc,
				   relopt_enum_elt_def *fc_members, int fc_default_val,
				   const char *fc_detailmsg, LOCKMODE fc_lockmode)
{
	relopt_enum *fc_newoption = fc_init_enum_reloption(fc_kinds, fc_name, fc_desc,
												 fc_members, fc_default_val,
												 fc_detailmsg, fc_lockmode);

	fc_add_reloption((relopt_gen *) fc_newoption);
}

/*
 * add_local_enum_reloption
 *		添加一个新的本地枚举 reloption
 *
 * 'offset' 是整型字段的偏移量。
 */
void add_local_enum_reloption(local_relopts *fc_relopts, const char *fc_name,
						 const char *fc_desc, relopt_enum_elt_def *fc_members,
						 int fc_default_val, const char *fc_detailmsg, int fc_offset)
{
	relopt_enum *fc_newoption = fc_init_enum_reloption(RELOPT_KIND_LOCAL,
												 fc_name, fc_desc,
												 fc_members, fc_default_val,
												 fc_detailmsg, 0);

	fc_add_local_reloption(fc_relopts, (relopt_gen *) fc_newoption, fc_offset);
}

/*
 * init_string_reloption
 *		分配并初始化一个新的字符串 reloption
 */
static relopt_string *
fc_init_string_reloption(bits32 fc_kinds, const char *fc_name, const char *fc_desc,
					  const char *fc_default_val,
					  validate_string_relopt fc_validator,
					  fill_string_relopt fc_filler,
					  LOCKMODE fc_lockmode)
{
	relopt_string *fc_newoption;

	/* 确保验证器/默认值组合是合理的 */
	if (fc_validator)
		(fc_validator) (fc_default_val);

	fc_newoption = (relopt_string *) fc_allocate_reloption(fc_kinds, RELOPT_TYPE_STRING,
													 fc_name, fc_desc, fc_lockmode);
	fc_newoption->validate_cb = fc_validator;
	fc_newoption->fill_cb = fc_filler;
	if (fc_default_val)
	{
		if (fc_kinds == RELOPT_KIND_LOCAL)
			fc_newoption->default_val = strdup(fc_default_val);
		else
			fc_newoption->default_val = MemoryContextStrdup(TopMemoryContext, fc_default_val);
		fc_newoption->default_len = strlen(fc_default_val);
		fc_newoption->default_isnull = false;
	}
	else
	{
		fc_newoption->default_val = "";
		fc_newoption->default_len = 0;
		fc_newoption->default_isnull = true;
	}

	return fc_newoption;
}

/*
 * add_string_reloption
 *		添加一个新的字符串 reloption
 *
 * “validator” 是一个可选的函数指针，用于测试值的有效性。如果参数字符串对变量不可接受，它必须 elog(ERROR)。请注意，默认值必须通过验证。
 */
void add_string_reloption(bits32 fc_kinds, const char *fc_name, const char *fc_desc,
					 const char *fc_default_val, validate_string_relopt fc_validator,
					 LOCKMODE fc_lockmode)
{
	relopt_string *fc_newoption = fc_init_string_reloption(fc_kinds, fc_name, fc_desc,
													 fc_default_val,
													 fc_validator, NULL,
													 fc_lockmode);

	fc_add_reloption((relopt_gen *) fc_newoption);
}

/*
 * add_local_string_reloption
 *		添加一个新的本地字符串 reloption
 *
 * 'offset' 是将存储字符串值在结果 bytea 结构中偏移量的整型字段的偏移量。
 */
void add_local_string_reloption(local_relopts *fc_relopts, const char *fc_name,
						   const char *fc_desc, const char *fc_default_val,
						   validate_string_relopt fc_validator,
						   fill_string_relopt fc_filler, int fc_offset)
{
	relopt_string *fc_newoption = fc_init_string_reloption(RELOPT_KIND_LOCAL,
													 fc_name, fc_desc,
													 fc_default_val,
													 fc_validator, fc_filler,
													 0);

	fc_add_local_reloption(fc_relopts, (relopt_gen *) fc_newoption, fc_offset);
}

/*
 * 将关系选项列表（DefElem 的列表）转换为文本数组格式，该格式保存在 pg_class.reloptions 中，只包括那些在传递的命名空间中的选项。输出值不包括命名空间。
 *
 * 这用于三种情况：CREATE TABLE/INDEX、ALTER TABLE SET 和 ALTER TABLE RESET。在 ALTER 情况下，oldOptions 是现有的 reloptions 值（可能为 NULL），我们根据需要替换或删除条目。
 *
 * 如果 acceptOidsOff 为 true，则我们允许 oids = false，但在为 true 时抛出错误。这仅仅是为了向后兼容。
 *
 * 注意，这不负责确定选项是否有效，但确实检查所有给定选项的命名空间是否在 validnsps 中列出。NULL 命名空间始终有效，无需显式列出。传入 NULL 指针表示只有 NULL 命名空间是有效的。
 *
 * oldOptions 和结果都是文本数组（或“默认”的 NULL），但我们将它们声明为 Datum，以避免在 reloptions.h 中包含 array.h。
 */
Datum transformRelOptions(Datum fc_oldOptions, List *fc_defList, const char *fc_namspace,
					char *fc_validnsps[], bool fc_acceptOidsOff, bool fc_isReset)
{
	Datum		fc_result;
	ArrayBuildState *fc_astate;
	ListCell   *fc_cell;

	/* 如果空列表则无变化 */
	if (fc_defList == NIL)
		return fc_oldOptions;

	/* 我们使用 accumArrayResult 构建新数组 */
	fc_astate = NULL;

	/* 复制任何不被替换的 oldOptions */
	if (PointerIsValid(DatumGetPointer(fc_oldOptions)))
	{
		ArrayType  *fc_array = DatumGetArrayTypeP(fc_oldOptions);
		Datum	   *fc_oldoptions;
		int			fc_noldoptions;
		int			fc_i;

		deconstruct_array(fc_array, TEXTOID, -1, false, TYPALIGN_INT,
						  &fc_oldoptions, NULL, &fc_noldoptions);

		for (fc_i = 0; fc_i < fc_noldoptions; fc_i++)
		{
			char	   *fc_text_str = VARDATA(fc_oldoptions[fc_i]);
			int			fc_text_len = VARSIZE(fc_oldoptions[fc_i]) - VARHDRSZ;

			/* 在 defList 中搜索匹配项 */
			foreach(fc_cell, fc_defList)
			{
				DefElem    *fc_def = (DefElem *) lfirst(fc_cell);
				int			fc_kw_len;

				/* 如果不在同一命名空间内则忽略 */
				if (fc_namspace == NULL)
				{
					if (fc_def->defnamespace != NULL)
						continue;
				}
				else if (fc_def->defnamespace == NULL)
					continue;
				else if (strcmp(fc_def->defnamespace, fc_namspace) != 0)
					continue;

				fc_kw_len = strlen(fc_def->defname);
				if (fc_text_len > fc_kw_len && fc_text_str[fc_kw_len] == '=' &&
					strncmp(fc_text_str, fc_def->defname, fc_kw_len) == 0)
					break;
			}
			if (!fc_cell)
			{
				/* 无匹配，因此保留旧选项 */
				fc_astate = accumArrayResult(fc_astate, fc_oldoptions[fc_i],
										  false, TEXTOID,
										  CurrentMemoryContext);
			}
		}
	}

	/*
	 * 如果是 CREATE/SET，将新选项添加到数组；如果是 RESET，仅检查用户没有说 RESET（option=val）。(必须这样做，因为语法不强制执行。)
	 */
	foreach(fc_cell, fc_defList)
	{
		DefElem    *fc_def = (DefElem *) lfirst(fc_cell);

		if (fc_isReset)
		{
			if (fc_def->arg != NULL)
				ereport(ERROR,
						(errcode(ERRCODE_SYNTAX_ERROR),
						 errmsg("RESET must not include values for parameters")));
		}
		else
		{
			text	   *fc_t;
			const char *fc_value;
			Size		fc_len;

			/*
			 * 如果命名空间无效则出错。NULL 命名空间始终有效。
			 */
			if (fc_def->defnamespace != NULL)
			{
				bool		fc_valid = false;
				int			fc_i;

				if (fc_validnsps)
				{
					for (fc_i = 0; fc_validnsps[fc_i]; fc_i++)
					{
						if (strcmp(fc_def->defnamespace, fc_validnsps[fc_i]) == 0)
						{
							fc_valid = true;
							break;
						}
					}
				}

				if (!fc_valid)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("unrecognized parameter namespace \"%s\"",
									fc_def->defnamespace)));
			}

			/* 如果不在同一命名空间内则忽略 */
			if (fc_namspace == NULL)
			{
				if (fc_def->defnamespace != NULL)
					continue;
			}
			else if (fc_def->defnamespace == NULL)
				continue;
			else if (strcmp(fc_def->defnamespace, fc_namspace) != 0)
				continue;

			/*
			 * 将DefElem展平为文本字符串，例如"name=arg"。如果我们只有"name"，则假定"名=true"是指的。注意：命名空间不输出。
			 */
			if (fc_def->arg != NULL)
				fc_value = defGetString(fc_def);
			else
				fc_value = "true";

			/*
			 * 这不是进行此测试的好地方，但没有其他方便的地方可以过滤掉该选项。由于WITH（oids=false）将来某一天会被删除，这似乎是可以接受的丑陋程度。
			 */
			if (fc_acceptOidsOff && fc_def->defnamespace == NULL &&
				strcmp(fc_def->defname, "oids") == 0)
			{
				if (defGetBoolean(fc_def))
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("tables declared WITH OIDS are not supported")));
				/* 跳过选项，reloptions机制不知道它 */
				continue;
			}

			fc_len = VARHDRSZ + strlen(fc_def->defname) + 1 + strlen(fc_value);
			/* +1 为sprintf的尾随空值留出空间 */
			fc_t = (text *) palloc(fc_len + 1);
			SET_VARSIZE(fc_t, fc_len);
			sprintf(VARDATA(fc_t), "%s=%s", fc_def->defname, fc_value);

			fc_astate = accumArrayResult(fc_astate, PointerGetDatum(fc_t),
									  false, TEXTOID,
									  CurrentMemoryContext);
		}
	}

	if (fc_astate)
		fc_result = makeArrayResult(fc_astate, CurrentMemoryContext);
	else
		fc_result = (Datum) 0;

	return fc_result;
}


/*
 * 将reloptions的文本数组格式转换为DefElem的列表。
 * 这是transformRelOptions()的逆过程。
 */
List * untransformRelOptions(Datum fc_options)
{
	List	   *fc_result = NIL;
	ArrayType  *fc_array;
	Datum	   *fc_optiondatums;
	int			fc_noptions;
	int			fc_i;

	/* 如果没有选项则无需操作 */
	if (!PointerIsValid(DatumGetPointer(fc_options)))
		return fc_result;

	fc_array = DatumGetArrayTypeP(fc_options);

	deconstruct_array(fc_array, TEXTOID, -1, false, TYPALIGN_INT,
					  &fc_optiondatums, NULL, &fc_noptions);

	for (fc_i = 0; fc_i < fc_noptions; fc_i++)
	{
		char	   *fc_s;
		char	   *fc_p;
		Node	   *fc_val = NULL;

		fc_s = TextDatumGetCString(fc_optiondatums[fc_i]);
		fc_p = strchr(fc_s, '=');
		if (fc_p)
		{
			*fc_p++ = '\0';
			fc_val = (Node *) makeString(pstrdup(fc_p));
		}
		fc_result = lappend(fc_result, makeDefElem(pstrdup(fc_s), fc_val, -1));
	}

	return fc_result;
}

/*
 * 从pg_class元组中提取和解析reloptions。
 *
 * 这是一个低级例程，预计由relcache代码和没有表的relcache条目的调用者使用（例如，autovacuum）。对于其他用途，请考虑从relcache条目获取rd_options指针。
 *
 * tupdesc是pg_class的元组描述符。amoptions是在对应于索引的元组的情况下，指向索引AM的选项解析函数的指针，否则为NULL。
 */
bytea * extractRelOptions(HeapTuple fc_tuple, TupleDesc fc_tupdesc,
				  amoptions_function fc_amoptions)
{
	bytea	   *fc_options;
	bool		fc_isnull;
	Datum		fc_datum;
	Form_pg_class fc_classForm;

	fc_datum = fastgetattr(fc_tuple,
						Anum_pg_class_reloptions,
						fc_tupdesc,
						&fc_isnull);
	if (fc_isnull)
		return NULL;

	fc_classForm = (Form_pg_class) GETSTRUCT(fc_tuple);

	/* 解析为适当格式；在这里不出错 */
	switch (fc_classForm->relkind)
	{
		case RELKIND_RELATION:
		case RELKIND_TOASTVALUE:
		case RELKIND_MATVIEW:
			fc_options = heap_reloptions(fc_classForm->relkind, fc_datum, false);
			break;
		case RELKIND_PARTITIONED_TABLE:
			fc_options = partitioned_table_reloptions(fc_datum, false);
			break;
		case RELKIND_VIEW:
			fc_options = view_reloptions(fc_datum, false);
			break;
		case RELKIND_INDEX:
		case RELKIND_PARTITIONED_INDEX:
			fc_options = index_reloptions(fc_amoptions, fc_datum, false);
			break;
		case RELKIND_FOREIGN_TABLE:
			fc_options = NULL;
			break;
		default:
			Assert(false);		/* 不能到这里 */
			fc_options = NULL;		/* 保持编译器安静 */
			break;
	}

	return fc_options;
}

static void fc_parseRelOptionsInternal(Datum fc_options, bool fc_validate,
						relopt_value *fc_reloptions, int fc_numoptions)
{
	ArrayType  *fc_array = DatumGetArrayTypeP(fc_options);
	Datum	   *fc_optiondatums;
	int			fc_noptions;
	int			fc_i;

	deconstruct_array(fc_array, TEXTOID, -1, false, TYPALIGN_INT,
					  &fc_optiondatums, NULL, &fc_noptions);

	for (fc_i = 0; fc_i < fc_noptions; fc_i++)
	{
		char	   *fc_text_str = VARDATA(fc_optiondatums[fc_i]);
		int			fc_text_len = VARSIZE(fc_optiondatums[fc_i]) - VARHDRSZ;
		int			fc_j;

		/* 在reloptions中搜索匹配项 */
		for (fc_j = 0; fc_j < fc_numoptions; fc_j++)
		{
			int			fc_kw_len = fc_reloptions[fc_j].gen->namelen;

			if (fc_text_len > fc_kw_len && fc_text_str[fc_kw_len] == '=' &&
				strncmp(fc_text_str, fc_reloptions[fc_j].gen->name, fc_kw_len) == 0)
			{
				fc_parse_one_reloption(&fc_reloptions[fc_j], fc_text_str, fc_text_len,
									fc_validate);
				break;
			}
		}

		if (fc_j >= fc_numoptions && fc_validate)
		{
			char	   *fc_s;
			char	   *fc_p;

			fc_s = TextDatumGetCString(fc_optiondatums[fc_i]);
			fc_p = strchr(fc_s, '=');
			if (fc_p)
				*fc_p = '\0';
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("unrecognized parameter \"%s\"", fc_s)));
		}
	}

	/* 避免在此函数中内存泄漏是值得的 */
	pfree(fc_optiondatums);

	if (((void *) fc_array) != DatumGetPointer(fc_options))
		pfree(fc_array);
}

/*
 * 解释以文本数组格式给出的reloptions。
 *
 * options是由transformRelOptions构建的reloption文本数组。
 * kind指定要处理的选项类别。
 *
 * 返回值是一个relopt_value *数组，在其中选项数组中实际设置的选项被标记为isset=true。此数组的长度将在*numrelopts中返回。未设置的选项也包含在数组中；这是为了使调用者可以轻松找到默认值。
 *
 * 如果没有给定类别的选项，则numrelopts被设置为0并返回NULL（除非尽管未定义选项，但仍然非法提供选项，此时会发生错误）。
 *
 * 注意：类型为int、bool和real的值作为返回数组的一部分进行分配。类型为string的值单独分配，必须由调用者释放。
 */
static relopt_value *
fc_parseRelOptions(Datum fc_options, bool fc_validate, relopt_kind fc_kind,
				int *fc_numrelopts)
{
	relopt_value *fc_reloptions = NULL;
	int			fc_numoptions = 0;
	int			fc_i;
	int			fc_j;

	if (need_initialization)
		fc_initialize_reloptions();

	/* 根据kind构建预期选项的列表 */

	for (fc_i = 0; relOpts[fc_i]; fc_i++)
		if (relOpts[fc_i]->kinds & fc_kind)
			fc_numoptions++;

	if (fc_numoptions > 0)
	{
		fc_reloptions = palloc(fc_numoptions * sizeof(relopt_value));

		for (fc_i = 0, fc_j = 0; relOpts[fc_i]; fc_i++)
		{
			if (relOpts[fc_i]->kinds & fc_kind)
			{
				fc_reloptions[fc_j].gen = relOpts[fc_i];
				fc_reloptions[fc_j].isset = false;
				fc_j++;
			}
		}
	}

	/* 如果没有选项，则完成 */
	if (PointerIsValid(DatumGetPointer(fc_options)))
		fc_parseRelOptionsInternal(fc_options, fc_validate, fc_reloptions, fc_numoptions);

	*fc_numrelopts = fc_numoptions;
	return fc_reloptions;
}

/* 解析本地未注册选项。 */
static relopt_value *
fc_parseLocalRelOptions(local_relopts *fc_relopts, Datum fc_options, bool fc_validate)
{
	int			fc_nopts = list_length(fc_relopts->options);
	relopt_value *fc_values = palloc(sizeof(*fc_values) * fc_nopts);
	ListCell   *fc_lc;
	int			fc_i = 0;

	foreach(fc_lc, fc_relopts->options)
	{
		local_relopt *fc_opt = lfirst(fc_lc);

		fc_values[fc_i].gen = fc_opt->option;
		fc_values[fc_i].isset = false;

		fc_i++;
	}

	if (fc_options != (Datum) 0)
		fc_parseRelOptionsInternal(fc_options, fc_validate, fc_values, fc_nopts);

	return fc_values;
}

/*
 * parseRelOptions的子例程，用于解析和验证单个选项的值
 */
static void fc_parse_one_reloption(relopt_value *fc_option, char *fc_text_str, int fc_text_len,
					bool fc_validate)
{
	char	   *fc_value;
	int			fc_value_len;
	bool		fc_parsed;
	bool		fc_nofree = false;

	if (fc_option->isset && fc_validate)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("parameter \"%s\" specified more than once",
						fc_option->gen->name)));

	fc_value_len = fc_text_len - fc_option->gen->namelen - 1;
	fc_value = (char *) palloc(fc_value_len + 1);
	memcpy(fc_value, fc_text_str + fc_option->gen->namelen + 1, fc_value_len);
	fc_value[fc_value_len] = '\0';

	switch (fc_option->gen->type)
	{
		case RELOPT_TYPE_BOOL:
			{
				fc_parsed = parse_bool(fc_value, &fc_option->values.bool_val);
				if (fc_validate && !fc_parsed)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("invalid value for boolean option \"%s\": %s",
									fc_option->gen->name, fc_value)));
			}
			break;
		case RELOPT_TYPE_INT:
			{
				relopt_int *fc_optint = (relopt_int *) fc_option->gen;

				fc_parsed = parse_int(fc_value, &fc_option->values.int_val, 0, NULL);
				if (fc_validate && !fc_parsed)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("invalid value for integer option \"%s\": %s",
									fc_option->gen->name, fc_value)));
				if (fc_validate && (fc_option->values.int_val < fc_optint->min ||
								 fc_option->values.int_val > fc_optint->max))
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("value %s out of bounds for option \"%s\"",
									fc_value, fc_option->gen->name),
							 errdetail("Valid values are between \"%d\" and \"%d\".",
									   fc_optint->min, fc_optint->max)));
			}
			break;
		case RELOPT_TYPE_REAL:
			{
				relopt_real *fc_optreal = (relopt_real *) fc_option->gen;

				fc_parsed = parse_real(fc_value, &fc_option->values.real_val, 0, NULL);
				if (fc_validate && !fc_parsed)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("invalid value for floating point option \"%s\": %s",
									fc_option->gen->name, fc_value)));
				if (fc_validate && (fc_option->values.real_val < fc_optreal->min ||
								 fc_option->values.real_val > fc_optreal->max))
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("value %s out of bounds for option \"%s\"",
									fc_value, fc_option->gen->name),
							 errdetail("Valid values are between \"%f\" and \"%f\".",
									   fc_optreal->min, fc_optreal->max)));
			}
			break;
		case RELOPT_TYPE_ENUM:
			{
				relopt_enum *fc_optenum = (relopt_enum *) fc_option->gen;
				relopt_enum_elt_def *fc_elt;

				fc_parsed = false;
				for (fc_elt = fc_optenum->members; fc_elt->string_val; fc_elt++)
				{
					if (pg_strcasecmp(fc_value, fc_elt->string_val) == 0)
					{
						fc_option->values.enum_val = fc_elt->symbol_val;
						fc_parsed = true;
						break;
					}
				}
				if (fc_validate && !fc_parsed)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
							 errmsg("invalid value for enum option \"%s\": %s",
									fc_option->gen->name, fc_value),
							 fc_optenum->detailmsg ?
							 errdetail_internal("%s", _(fc_optenum->detailmsg)) : 0));

				/*
				 * 如果值不在允许的字符串值中，但我们没有要求进行验证，则只需使用默认的数值。
				 */
				if (!fc_parsed)
					fc_option->values.enum_val = fc_optenum->default_val;
			}
			break;
		case RELOPT_TYPE_STRING:
			{
				relopt_string *fc_optstring = (relopt_string *) fc_option->gen;

				fc_option->values.string_val = fc_value;
				fc_nofree = true;
				if (fc_validate && fc_optstring->validate_cb)
					(fc_optstring->validate_cb) (fc_value);
				fc_parsed = true;
			}
			break;
		default:
			elog(ERROR, "unsupported reloption type %d", fc_option->gen->type);
			fc_parsed = true;		/* 让编译器安静 */
			break;
	}

	if (fc_parsed)
		fc_option->isset = true;
	if (!fc_nofree)
		pfree(fc_value);
}

/*
 * 给定parseRelOptions的结果，分配一个结构，该结构的基本大小加上字符串变量所需的额外空间。
 *
 * “base”应为reloptions结构（StdRdOptions或等效结构）的sizeof(struct)。
 */
static void * fc_allocateReloptStruct(Size fc_base, relopt_value *fc_options, int fc_numoptions)
{
	Size		fc_size = fc_base;
	int			fc_i;

	for (fc_i = 0; fc_i < fc_numoptions; fc_i++)
	{
		relopt_value *fc_optval = &fc_options[fc_i];

		if (fc_optval->gen->type == RELOPT_TYPE_STRING)
		{
			relopt_string *fc_optstr = (relopt_string *) fc_optval->gen;

			if (fc_optstr->fill_cb)
			{
				const char *fc_val = fc_optval->isset ? fc_optval->values.string_val :
				fc_optstr->default_isnull ? NULL : fc_optstr->default_val;

				fc_size += fc_optstr->fill_cb(fc_val, NULL);
			}
			else
				fc_size += GET_STRING_RELOPTION_LEN(*fc_optval) + 1;
		}
	}

	return palloc0(fc_size);
}

/*
 * 根据parseRelOptions的结果和解析表，使用解析值填充
 * 结构（之前使用allocateReloptStruct分配）。
 *
 * rdopts是指向要填充的已分配结构的指针。
 * basesize是传递给allocateReloptStruct的struct的大小。
 * options，长度为numoptions，是parseRelOptions的输出。
 * elems，长度为numelems，是描述允许选项的表。
 * 当validate为真时，预期所有选项都出现在elems中。
 */
static void fc_fillRelOptions(void *fc_rdopts, Size fc_basesize,
			   relopt_value *fc_options, int fc_numoptions,
			   bool fc_validate,
			   const relopt_parse_elt *fc_elems, int fc_numelems)
{
	int			fc_i;
	int			fc_offset = fc_basesize;

	for (fc_i = 0; fc_i < fc_numoptions; fc_i++)
	{
		int			fc_j;
		bool		fc_found = false;

		for (fc_j = 0; fc_j < fc_numelems; fc_j++)
		{
			if (strcmp(fc_options[fc_i].gen->name, fc_elems[fc_j].optname) == 0)
			{
				relopt_string *fc_optstring;
				char	   *fc_itempos = ((char *) fc_rdopts) + fc_elems[fc_j].offset;
				char	   *fc_string_val;

				switch (fc_options[fc_i].gen->type)
				{
					case RELOPT_TYPE_BOOL:
						*(bool *) fc_itempos = fc_options[fc_i].isset ?
							fc_options[fc_i].values.bool_val :
							((relopt_bool *) fc_options[fc_i].gen)->default_val;
						break;
					case RELOPT_TYPE_INT:
						*(int *) fc_itempos = fc_options[fc_i].isset ?
							fc_options[fc_i].values.int_val :
							((relopt_int *) fc_options[fc_i].gen)->default_val;
						break;
					case RELOPT_TYPE_REAL:
						*(double *) fc_itempos = fc_options[fc_i].isset ?
							fc_options[fc_i].values.real_val :
							((relopt_real *) fc_options[fc_i].gen)->default_val;
						break;
					case RELOPT_TYPE_ENUM:
						*(int *) fc_itempos = fc_options[fc_i].isset ?
							fc_options[fc_i].values.enum_val :
							((relopt_enum *) fc_options[fc_i].gen)->default_val;
						break;
					case RELOPT_TYPE_STRING:
						fc_optstring = (relopt_string *) fc_options[fc_i].gen;
						if (fc_options[fc_i].isset)
							fc_string_val = fc_options[fc_i].values.string_val;
						else if (!fc_optstring->default_isnull)
							fc_string_val = fc_optstring->default_val;
						else
							fc_string_val = NULL;

						if (fc_optstring->fill_cb)
						{
							Size		fc_size =
							fc_optstring->fill_cb(fc_string_val,
											   (char *) fc_rdopts + fc_offset);

							if (fc_size)
							{
								*(int *) fc_itempos = fc_offset;
								fc_offset += fc_size;
							}
							else
								*(int *) fc_itempos = 0;
						}
						else if (fc_string_val == NULL)
							*(int *) fc_itempos = 0;
						else
						{
							strcpy((char *) fc_rdopts + fc_offset, fc_string_val);
							*(int *) fc_itempos = fc_offset;
							fc_offset += strlen(fc_string_val) + 1;
						}
						break;
					default:
						elog(ERROR, "unsupported reloption type %d",
							 fc_options[fc_i].gen->type);
						break;
				}
				fc_found = true;
				break;
			}
		}
		if (fc_validate && !fc_found)
			elog(ERROR, "reloption \"%s\" not found in parse table",
				 fc_options[fc_i].gen->name);
	}
	SET_VARSIZE(fc_rdopts, fc_offset);
}


/*
 * 用于任何使用StdRdOptions的选项解析器。
 */
bytea * default_reloptions(Datum fc_reloptions, bool fc_validate, relopt_kind fc_kind)
{
	static const relopt_parse_elt fc_tab[] = {
		{"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
		{"autovacuum_enabled", RELOPT_TYPE_BOOL,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
		{"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)},
		{"autovacuum_vacuum_insert_threshold", RELOPT_TYPE_INT,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_threshold)},
		{"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
		{"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
		{"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
		{"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
		{"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
		{"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)},
		{"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
		{"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
		{"log_autovacuum_min_duration", RELOPT_TYPE_INT,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
		{"toast_tuple_target", RELOPT_TYPE_INT,
		offsetof(StdRdOptions, toast_tuple_target)},
		{"autovacuum_vacuum_cost_delay", RELOPT_TYPE_REAL,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},
		{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
		{"autovacuum_vacuum_insert_scale_factor", RELOPT_TYPE_REAL,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_scale_factor)},
		{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
		offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)},
		{"user_catalog_table", RELOPT_TYPE_BOOL,
		offsetof(StdRdOptions, user_catalog_table)},
		{"parallel_workers", RELOPT_TYPE_INT,
		offsetof(StdRdOptions, parallel_workers)},
		{"vacuum_index_cleanup", RELOPT_TYPE_ENUM,
		offsetof(StdRdOptions, vacuum_index_cleanup)},
		{"vacuum_truncate", RELOPT_TYPE_BOOL,
		offsetof(StdRdOptions, vacuum_truncate)}
	};

	return (bytea *) build_reloptions(fc_reloptions, fc_validate, fc_kind,
									  sizeof(StdRdOptions),
									  fc_tab, lengthof(fc_tab));
}

/*
 * build_reloptions
 *
 * 解析调用者提供的"reloptions"，并将其返回到
 * 包含解析选项的结构中。解析是借助描述
 * 允许选项的解析表进行的，该表由长度为
 * "num_relopt_elems"的"relopt_elems"定义。
 *
 * 如果reloptions值是由transformRelOptions()新建的，则
 * "validate"必须为真，而不是从目录中读取，在这种情况下
 * 其中包含的值必须已经有效。
 *
 * 如果传入的选项与解析表中的任何选项不匹配，则返回NULL，
 * 除非validate为真，在这种情况下将报告一个错误。
 */
void * build_reloptions(Datum fc_reloptions, bool fc_validate,
				 relopt_kind fc_kind,
				 Size fc_relopt_struct_size,
				 const relopt_parse_elt *fc_relopt_elems,
				 int fc_num_relopt_elems)
{
	int			fc_numoptions;
	relopt_value *fc_options;
	void	   *fc_rdopts;

	/* 解析特定于给定关系选项类型的选项 */
	fc_options = fc_parseRelOptions(fc_reloptions, fc_validate, fc_kind, &fc_numoptions);
	Assert(fc_numoptions <= fc_num_relopt_elems);

	/* 如果没有设置，我们就完成了 */
	if (fc_numoptions == 0)
	{
		Assert(fc_options == NULL);
		return NULL;
	}

	/* 分配并填充结构 */
	fc_rdopts = fc_allocateReloptStruct(fc_relopt_struct_size, fc_options, fc_numoptions);
	fc_fillRelOptions(fc_rdopts, fc_relopt_struct_size, fc_options, fc_numoptions,
				   fc_validate, fc_relopt_elems, fc_num_relopt_elems);

	pfree(fc_options);

	return fc_rdopts;
}

/*
 * 解析本地选项，分配一个字节结构，其大小为指定的
 * 'base_size'加上所需的任何字符串变量的额外空间，
 * 填充在给定偏移量处的选项字段并返回它。
 */
void * build_local_reloptions(local_relopts *fc_relopts, Datum fc_options, bool fc_validate)
{
	int			fc_noptions = list_length(fc_relopts->options);
	relopt_parse_elt *fc_elems = palloc(sizeof(*fc_elems) * fc_noptions);
	relopt_value *fc_vals;
	void	   *fc_opts;
	int			fc_i = 0;
	ListCell   *fc_lc;

	foreach(fc_lc, fc_relopts->options)
	{
		local_relopt *fc_opt = lfirst(fc_lc);

		fc_elems[fc_i].optname = fc_opt->option->name;
		fc_elems[fc_i].opttype = fc_opt->option->type;
		fc_elems[fc_i].offset = fc_opt->offset;

		fc_i++;
	}

	fc_vals = fc_parseLocalRelOptions(fc_relopts, fc_options, fc_validate);
	fc_opts = fc_allocateReloptStruct(fc_relopts->relopt_struct_size, fc_vals, fc_noptions);
	fc_fillRelOptions(fc_opts, fc_relopts->relopt_struct_size, fc_vals, fc_noptions, fc_validate,
				   fc_elems, fc_noptions);

	if (fc_validate)
		foreach(fc_lc, fc_relopts->validators)
			((relopts_validator) lfirst(fc_lc)) (fc_opts, fc_vals, fc_noptions);

	if (fc_elems)
		pfree(fc_elems);

	return fc_opts;
}

/*
 * 用于分区表的选项解析器
 */
bytea * partitioned_table_reloptions(Datum fc_reloptions, bool fc_validate)
{
	/*
	 * 目前分区表没有选项，但这个可以执行一些验证。
	 */
	return (bytea *) build_reloptions(fc_reloptions, fc_validate,
									  RELOPT_KIND_PARTITIONED,
									  0, NULL, 0);
}

/*
 * 用于视图的选项解析器
 */
bytea * view_reloptions(Datum fc_reloptions, bool fc_validate)
{
	static const relopt_parse_elt fc_tab[] = {
		{"security_barrier", RELOPT_TYPE_BOOL,
		offsetof(ViewOptions, security_barrier)},
		{"security_invoker", RELOPT_TYPE_BOOL,
		offsetof(ViewOptions, security_invoker)},
		{"check_option", RELOPT_TYPE_ENUM,
		offsetof(ViewOptions, check_option)}
	};

	return (bytea *) build_reloptions(fc_reloptions, fc_validate,
									  RELOPT_KIND_VIEW,
									  sizeof(ViewOptions),
									  fc_tab, lengthof(fc_tab));
}

/*
 * 解析堆、视图和toast表的选项。
 */
bytea * heap_reloptions(char fc_relkind, Datum fc_reloptions, bool fc_validate)
{
	StdRdOptions *fc_rdopts;

	switch (fc_relkind)
	{
		case RELKIND_TOASTVALUE:
			fc_rdopts = (StdRdOptions *)
				default_reloptions(fc_reloptions, fc_validate, RELOPT_KIND_TOAST);
			if (fc_rdopts != NULL)
			{
				/* 调整TOAST关系的仅默认参数 */
				fc_rdopts->fillfactor = 100;
				fc_rdopts->autovacuum.analyze_threshold = -1;
				fc_rdopts->autovacuum.analyze_scale_factor = -1;
			}
			return (bytea *) fc_rdopts;
		case RELKIND_RELATION:
		case RELKIND_MATVIEW:
			return default_reloptions(fc_reloptions, fc_validate, RELOPT_KIND_HEAP);
		default:
			/* 不支持其他relkind */
			return NULL;
	}
}


/*
 * 解析索引的选项。
 *
 *	amoptions	index AM的选项解析函数
 *	reloptions	选项作为text[]数据
 *	validate	错误标志
 */
bytea * index_reloptions(amoptions_function fc_amoptions, Datum fc_reloptions, bool fc_validate)
{
	Assert(fc_amoptions != NULL);

	/* 假设函数是严格的 */
	if (!PointerIsValid(DatumGetPointer(fc_reloptions)))
		return NULL;

	return fc_amoptions(fc_reloptions, fc_validate);
}

/*
 * 用于属性reloptions的选项解析器
 */
bytea * attribute_reloptions(Datum fc_reloptions, bool fc_validate)
{
	static const relopt_parse_elt fc_tab[] = {
		{"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
		{"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
	};

	return (bytea *) build_reloptions(fc_reloptions, fc_validate,
									  RELOPT_KIND_ATTRIBUTE,
									  sizeof(AttributeOpts),
									  fc_tab, lengthof(fc_tab));
}

/*
 * 用于表空间reloptions的选项解析器
 */
bytea * tablespace_reloptions(Datum fc_reloptions, bool fc_validate)
{
	static const relopt_parse_elt fc_tab[] = {
		{"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
		{"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)},
		{"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)},
		{"maintenance_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, maintenance_io_concurrency)}
	};

	return (bytea *) build_reloptions(fc_reloptions, fc_validate,
									  RELOPT_KIND_TABLESPACE,
									  sizeof(TableSpaceOpts),
									  fc_tab, lengthof(fc_tab));
}

/*
 * 从选项列表中确定所需的LOCKMODE。
 *
 * 从AlterTableGetLockLevel()调用，查看该函数
 * 以获取有关如何工作的更长解释。
 */
LOCKMODE
AlterTableGetRelOptionsLockLevel(List *fc_defList)
{
	LOCKMODE	fc_lockmode = NoLock;
	ListCell   *fc_cell;

	if (fc_defList == NIL)
		return AccessExclusiveLock;

	if (need_initialization)
		fc_initialize_reloptions();

	foreach(fc_cell, fc_defList)
	{
		DefElem    *fc_def = (DefElem *) lfirst(fc_cell);
		int			fc_i;

		for (fc_i = 0; relOpts[fc_i]; fc_i++)
		{
			if (strncmp(relOpts[fc_i]->name,
						fc_def->defname,
						relOpts[fc_i]->namelen + 1) == 0)
			{
				if (fc_lockmode < relOpts[fc_i]->lockmode)
					fc_lockmode = relOpts[fc_i]->lockmode;
			}
		}
	}

	return fc_lockmode;
}
